package elements;
import items.Item;

import java.util.HashSet;
import java.util.List;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;

import command.Command;

import roborally.Board;
import roborally.Compiler;
import roborally.Direction;
import roborally.EnergyAmount;
import roborally.EnergyUnit;
import roborally.Entry;
import roborally.Orientation;
import roborally.Position;


import exceptions.IllegalPlacementException;
import exceptions.IllegalTerminatedException;
import be.kuleuven.cs.som.annotate.*;

/**
 * A class of robots with an orientation and an amount of energy.
 * 
 * @invar	The robot must have a legal amount of energy.
 * 			| this.isLegalEnergy(this.getEnergy())
 * @invar 	The robot must have a legal amount of maxEnergy
 * 			| this.isLegalMaxEnergy(this.getMaxEnergy())
 * 
 * @version	3.0
 * @author 	Lies Tambeur 	r0264469	2Bcwsbbh
 * @author	Eline Vanrykel	r0264543	2Bcwselt
 *
 */
public class Robot extends Element implements IEnergyHolder{
	/**
	 * The energy of the robot.
	 */
	private EnergyAmount energy;
	
	/**
	 * The orientation of the robot.
	 */
	private Orientation orientation;
	
	/**
	 * A list containing the items that the robot holds.
	 * @invar	The list is sorted from heavy to light.
	 * 			| for i from 0 to items.size()-2
	 * 			|	items.get(i).getWeight()=>items.get(i+1).getWeight()
	 */
	private LinkedList<Item> items;
	
	/**
	 * The EnergyAmount reflecting the energy required for 1 turn of a robot.
	 * @invar	The amount of the energy has to be zero or a positive value.
	 * 			|  ENERGY_FOR_TURN.getAmount() >= 0  
	 */
	private static final EnergyAmount ENERGY_FOR_TURN = new EnergyAmount(100,EnergyUnit.WATTSECONDS);
	/**
	 * The EnergyAmount reflecting the energy required for 1 move of a robot.
	 * @invar	The amount of the variable has to be zero or a positive value.
	 * 			|  ENERGY_FOR_MOVE.getAmount() >= 0
	 */
	private static final EnergyAmount ENERGY_FOR_MOVE = new EnergyAmount(500,EnergyUnit.WATTSECONDS);
	/**
	 * The EnergyAmount reflecting the maximum EnergyAmount of maximum energy a robot can possess.
	 * @invar	The amount of the variable has to be a positive value.
	 * 			|  MAX_MAXENERGY.getAmount() > 0
	 */
	private static final EnergyAmount MAX_MAXENERGY = new EnergyAmount(20000,EnergyUnit.WATTSECONDS);
	/**
	 * The EnergyAmount reflecting the energy required for 1 shot.
	 * @invar	The amount of the variable has to be zero or a positive value.
	 * 			| ENERGY_FOR_SHOT.getAmount() >= 0
	 */
	private static final EnergyAmount ENERGY_FOR_SHOT = new EnergyAmount(1000,EnergyUnit.WATTSECONDS);
	
	/**
	 * The maximum EnergyAmount for this robot.
	 */
	private EnergyAmount maxEnergy;
	
	/**
	 * The program the robot possesses.
	 */
	private Entry program;
	
	/**
	 * The EnergyAmount reflecting the energy required for 1 shot of a robot.
	 * @invar	The amount of the variable has to be zero or a positive value.
	 * 			|  ENERGY_FOR_SHOT.getAmount() >= 0
	 */	
	@Basic @Immutable
	public static EnergyAmount getEnergyRequiredToShoot(){
		return ENERGY_FOR_SHOT;
	}
	
	/**
	 * Returns the EnergyAmount reflecting the energy required for 1 turn of a robot.
	 * @return	The EnergyAmount reflecting the energy required for 1 turn of a robot.
	 * 			| result == ENERY_FOR_TURN
	 */
	@Basic @Immutable
	public static final EnergyAmount getEnergyForTurn(){
		return ENERGY_FOR_TURN;
	}
	
	/**
	 * Returns the EnergyAmount required for 1 move of this robot, taken into account that for every kg of items that the robot holds, it need 50 Ws more.
	 * @return 	The  EnergyAmount required for 1 move of this robot.
	 * 			| result == ENERGY_FOR_MOVE.addAndReturn(new EnergyAmount(0.05*this.getLoad(),EnergyUnit.WATTSECONDS))
	 */
	public EnergyAmount getEnergyForMove(){
		EnergyAmount result  = ENERGY_FOR_MOVE.getCopy();
		return result.addAndReturn(new EnergyAmount(0.05*this.getLoad(),EnergyUnit.WATTSECONDS));
	}
	
	/**
	 * Returns the EnergyAmount reflecting the maximum EnergyAmount a robot can possess.  
	 * @return	The EnergyAmount reflecting the maximum EnergyAmount a robot can possess.  
	 * 			| result == this.maxEnergy
	 */
	@Basic
	public EnergyAmount getMaxEnergy(){ 
		return maxEnergy;
	}
	
	/**
	 * Increases the maximum energy of this particular robot.
	 * @param 	amount
	 * 			The amount of energy to added to the current maximum amount of energy of
	 * 			this particular robot.
	 * @pre		Amount should be a positive amount of energy.
	 * 			| new EnergyAmount().isSmallerThanOrEqualTo(amount)
	 * @pre		The sum of amount and the current maximum amount of energy of this robot is 
	 * 			legal for this particular robot.
	 * 			| this.isLegalMaxEnergy(this.getMaxEnergy().addAndReturn(amount))
	 * @post	The maximum energy of the robot will be increased by amount/
	 * 			| new.getMaxEnergy() == this.getMaxEnergy().addAndReturn(amount) 
	 */
	public void increaseMaxEnergy(EnergyAmount amount){ 
		assert new EnergyAmount().isSmallerThanOrEqualTo(amount);
		assert this.isLegalMaxEnergy(this.getMaxEnergy().addAndReturn(amount));
		this.setMaxEnergy(amount.addAndReturn(this.getMaxEnergy()));
	}
	
	/**
	 * Sets the maximum amount of energy for this robot.
	 * @param	amount
	 * 			The amount of energy to be set as the maximum amount of energy for this robot.
	 * @pre		The given amount must be a legal maxEnergy.
	 * 			| this.isLegalMaxEnergy(amount)
	 * @post	The maximum amount of energy will be equal to amount.
	 * 			| new.getMaxEnergy() == amount
	 */
	@Model
	private void setMaxEnergy(EnergyAmount amount) {
		assert this.isLegalMaxEnergy(amount);
		this.maxEnergy = amount;
	}

	/**
	 * Decreases the maximum energy of this particular robot.
	 * @param 	amount
	 * 			The amount of energy to subtracted from the current maximum amount of energy of
	 * 			this particular robot.
	 * @pre		Amount should be a positive amount of energy or equal to zero.
	 * 			| !amount.isSmallerThan(new EnergyAmount())
	 * @effect	The robot will be terminated if the maximum amount of energy reaches zero
	 * 			or a negative amount.
	 * 			| ((this.getMaxEnergy().subtractAndReturn(amount)).isSmallerThanOrEqualTo(new EnergyAmount())) == (this.terminate())
	 * @post	The maximum energy of the robot will be decreased by amount.
	 * 			| new.getMaxEnergy() == this.getMaxEnergy().subtractAndReturn(amount) 
	 */
	@Model
	private void decreaseMaxEnergy(EnergyAmount amount){
		assert !amount.isSmallerThan(new EnergyAmount());
		if((this.getMaxEnergy().subtractAndReturn(amount)).isSmallerThanOrEqualTo(new EnergyAmount())){
			this.terminate();
			System.err.println("Robot was terminated due to being hit!");
		}
		else{
			this.setMaxEnergy(this.getMaxEnergy().subtractAndReturn(amount));
		}
	}
	
	/**
	 * Checks whether a given amount of max energy is legal for this
	 * particular robot.
	 * @param 	energy
	 * 			The amount of energy to be checked.
	 * @return	True only is energy is a positive amount and is smaller than or equal to
	 * 			the maximum amount of maximum energy the robot can possess.
	 * 			| result == (!energy.isSmallerThan(new EnergyAmount())) && (energy.isSmallerThanOrEqualTo(MAX_MAXENERGY) && energy!=null)
	 */
	@Model
	private boolean isLegalMaxEnergy(EnergyAmount energy) {
		return (!energy.isSmallerThan(new EnergyAmount())) && (energy.isSmallerThanOrEqualTo(MAX_MAXENERGY) && energy!=null);
	}
	
	/**
	 * Returns the maximum value the robot's maximum energy can reach.
	 * @return	The maximum value the robot's maximum energy can reach.
	 * 			| result == MAX_MAXENERGY
	 */
	@Basic @Immutable
	public static final EnergyAmount getMaxMaxEnergy(){
		return MAX_MAXENERGY;
	}

	/**
	 * Initialize this new robot with the given energy and orientation.
	 * @param 	orientation
	 * 			The orientation for this new robot.
	 * @param 	energy
	 * 			The EnergyAmount for this new robot.
	 * @effect	The orientation of the robot is set to the given orientation.
	 * 			| this.setOrientation(orientation)
	 * @effect	If the given energy is legal, the energy of the robot will be set on the given energy. 
	 * 			Else the energy of the robot will be set on MAX_ENERGY.  
	 * 			| if(isLegalEnergy(energy))
	 * 			|	then this.setEnergy(energy)
	 * 			| else this.setEnergy(this.getMaxEnergy())
	 */
	public Robot(Orientation orientation, EnergyAmount energy){
		maxEnergy = new EnergyAmount(15000, EnergyUnit.WATTSECONDS);
		this.setOrientation(orientation);
		if(this.isLegalEnergy(energy)){
			this.setEnergy(energy);
		}
		else{
			this.setEnergy(this.getMaxEnergy());
		}
		items = new LinkedList<Item>();
		program = null;
	}
	
	/**
	 * Initialize this new robot.
	 * 			| this(Orientation.RIGHT, MAX_ENERGY)
	 */
	public Robot(){
		this(Orientation.RIGHT,new EnergyAmount(15000,EnergyUnit.WATTSECONDS));  
	}
	
	/**
	 * Returns the EnergyAmount of energy of the robot.
	 * @return	The current EnergyAmount of the robot.
	 * 			| result == this.energy
	 */
	@Basic @Override
	public EnergyAmount getEnergy() {
		return energy;
	}
	
	/**
	 * Sets the energy of the robot to the given EnergyAmount, if the given EnergyAmount is valid.
	 * @param 	energy
	 * 			The new EnergyAmount for this robot.
	 * @pre	The given energy must be a valid EnergyAmount.
	 * 		| this.isLegalEnergy(energy)
	 * @post	The energy of this robot is equal to the given energy.
     *       	| new.getEnergy() == energy
	 */
	@Override
	public void setEnergy(EnergyAmount energy) {
		assert this.isLegalEnergy(energy); 
		this.energy = energy;
	}
	
	/**
	 * Returns the orientation of the robot.
	 * @return	The orientation of the robot.
	 * 			| result == this.orientation
	 */
	@Basic
	public Orientation getOrientation() {
		return orientation;
	}
	
	/**
	 * Sets the orientation of the robot to the given orientation if it isn't null, else the orientation of the robot will be set right.
	 * @param 	orientation
	 * @post	The orientation of this robot is equal to the given orientation if the given orientation isn't null. If it is null it will be set right.
	 * 			| if(orientation!=null)
	 * 			|	then new.getOrientation() == orientation
	 * 			| else new.getOrientation()==Orientation.RIGHT
	 */
	@Model
	private void setOrientation(Orientation orientation){
		if(orientation==null){
			this.orientation = Orientation.RIGHT;
		}
		else{
			this.orientation = orientation;
		}
	}
	
	/**
	 * Checks whether a given energy is a valid energy.
	 * @param 	energy
	 * 			The EnergyAmount that will be checked if it is a legal energy.
	 * @return	True if and only if the given energy is positive or equal to zero and smaller 
	 * 			or equal than the maximum energy a robot can possess.
	 * 			| result == !(energy.isSmallerThan(new EnergyAmount()))) && (!(maxEnergy.isSmallerThan(energy)) && energy!=null
	 */
	@Override
	public boolean isLegalEnergy(EnergyAmount energy) {
		return (!(energy.isSmallerThan(new EnergyAmount()))) && (!(maxEnergy.isSmallerThan(energy)) && energy!=null);
	}
	
	/**
	 * Returns the EnergyAmount as a fraction of the maximum EnergyAmount.
	 * @pre	The amount of MAX_ENERGY is not zero.
	 * 		| !this.getMaxEnergy().equals(new EnergyAmount())
	 * @return	The energy as a fraction of the maximum energy.
	 * 			| result == new EnergyAmount(this.getEnergy().toEnergyUnit(EnergyUnit.WATTSECONDS).getAmount()
				|	/this.getMaxEnergy().toEnergyUnit(EnergyUnit.WATTSECONDS).getAmount(),EnergyUnit.WATTSECONDS)
	 */
	public EnergyAmount getRelativeEnergy(){
		assert !this.getMaxEnergy().equals(new EnergyAmount());
		return new EnergyAmount(this.getEnergy().toEnergyUnit(EnergyUnit.WATTSECONDS).getAmount()
				/this.getMaxEnergy().toEnergyUnit(EnergyUnit.WATTSECONDS).getAmount(),EnergyUnit.WATTSECONDS);
	}
	
	/**
	 * Increase the energy with a given energy.
	 * @param 	energy
	 * 			The energy to be added to the current energy.
	 * @pre		The sum of the given energy and the current energy must be legal.
	 * 			| isLegalEnergy(this.getEnergy().addAndReturn(energy))
	 * @effect	The energy is set to its current contents
     *         	incremented with the given energy.
     *       	| this.setEnergy(energy.addAndReturn(this.getEnergy())) 
	 */
	@Override
	public void recharge(EnergyAmount energy){
		assert isLegalEnergy(this.getEnergy().addAndReturn(energy));
		this.setEnergy(energy.addAndReturn(this.getEnergy()));
	}
	
	/**
	 * Decrease the energy with a given energy.
	 * @param 	energy
	 * 			The energy to be subtracted from the current energy.
	 * @pre		The difference of the current energy and the given energy must be legal.
	 * 			| isLegalEnergy(this.getEnergy().subtractAndReturn(energy))
	 * @effect	The energy is set to its current contents
     *         	decremented with the given energy.
     *       	| this.setEnergy(energy.subtractAndReturn(this.getEnergy()))
	 */
	@Override
	public void decrease(EnergyAmount energy){
		assert isLegalEnergy(this.getEnergy().subtractAndReturn(energy));
		this.setEnergy(energy.subtractAndReturn(this.getEnergy()));
	}
	
	/**
	 * Transfers the given amount of energy from the other energyholder to this robot.  
	 * @param 	other
	 * 			The other energyholder from which the energy will be transfered.
	 * @param 	amount
	 * 			The EnergyAmount that will be transfered.
	 * @pre	The robot should have other in his possession.
	 * 		| this.getItems().contains(other)
	 * @pre	The sum of the energy from this robot and the given energy must be legal.
	 * 		| isLegalEnergy(this.getEnergy().addAndReturn(amount))
	 * @pre	The energy from the other energyholder decreased with the amount of energy to be transfered is legal.
	 * 		| other.isLegalEnergy(other.getEnergy() - amount)
	 * @pre	The given amount has to be positive or zero.
	 * 		| amount>=0
	 * @throws	IllegalTerminatedException
	 * 			When the robot is terminated.
	 * 			| this.isTerminated()
	 * @effect	The energy from this robot will be increased with the amount of energy to be transfered.
	 * 			| this.setEnergy(amount+this.getEnergy())
	 * @effect	The energy from the other energyholder will be decreased with the amount of energy to be transfered.
	 * 			| other.setEnergy(other.getEnergy()- amount)
	 */
	public void transferEnergy(IEnergyHolder other, EnergyAmount amount) throws IllegalTerminatedException { 
		if(this.isTerminated()){
			throw new IllegalTerminatedException();
		}
		assert(this.getItems().contains(other));
		assert(isLegalEnergy(this.getEnergy().addAndReturn(amount)));
		this.recharge(amount);
		other.decrease(amount); 
	}
	
	/**
	 * Turns the robot to the given direction.
	 * @param 	direction
	 * 			The direction to be turned to.
	 * @effect	The robot's orientation will be set on the current orientation turned in the given direction.
	 * 			| this.setOrientation(this.getOrientation().turn(direction))
	 * @effect	The robot's energy will be set on the current energy dercreased with the energy for one turn.
	 * 			| this.setEnergy(this.getEnergy().subtractAndReturn(Robot.getEnergyForTurn()))
	 * @pre		The current energy decreased with the energy for one turn must be legal.
	 * 			| isLegalEnergy(this.getEnergy().subtractAndReturn(Robot.getEnergyForTurn()))
	 * @throws 	IllegalTerminatedException
	 * 			| this.isTerminated()
	 */
	public void turn(Direction direction) throws IllegalTerminatedException{
		if(this.isTerminated()){ 
			throw new IllegalTerminatedException();
		}
		assert (isLegalEnergy(this.getEnergy().subtractAndReturn(Robot.getEnergyForTurn())));
			this.setOrientation(this.getOrientation().turn(direction));
			this.setEnergy(this.getEnergy().subtractAndReturn(Robot.getEnergyForTurn()));
	}
	
	/**
	 * Moves the robot one place forward, depending on its orientation.
	 * @pre	The robot has sufficient energy to move.
	 * 		| isLegalEnergy(this.getEnergy().subtractAndReturn(this.getEnergyForMove()))
	 * @effect	The amount of energy of the robot will be decreased with the energy needed for 1 move.
	 * 			| this.setEnergy(this.getEnergy().subtractAndReturn(this.getEnergyForMove()))
	 * @effect	If the neighbour of the robot is a valid position, the robot will be removed from the board and then 
	 * 			put on the board at the position of the neighbour.
	 * 			| if(this.getBoard().isValidPosition(this.getPosition().getNeighbour(this.getOrientation())))
	 * 			|	then this.getBoard.removeElement(this)
	 * 			|	     this.getBoard.putElement(this.getPosition().getNeighbour(this.getOrientation()), this)
	 * @throws	IllegalStateException 
	 * 			| this.isTerminated() || this.getBoard() == null
	 * @throws	IllegalPlacementException
	 * 			| !this.getBoard().isValidPosition(this.getPosition().getNeighbour(this.getOrientation()))
	 * @throws	IllegalTerminatedException	
	 * 			When the robot is terminated.
	 * 			| this.isTerminated()
	 */
	public void move() throws IllegalStateException,IllegalPlacementException, IllegalTerminatedException {
		if(this.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(this.getBoard() == null){
			throw new IllegalStateException();
		}
		assert (isLegalEnergy(this.getEnergy().subtractAndReturn(this.getEnergyForMove())));  
			Board theBoard = this.getBoard();
			Position neighbour = null;
			Position curPosition = this.getPosition();
			neighbour = curPosition.getNeighbour(this.getOrientation());
			theBoard.removeElement(this);
			try{
				theBoard.putElement(neighbour, this); //gooit IllegalPlacementException als !isValidPosition
				this.setEnergy(this.getEnergy().subtractAndReturn(this.getEnergyForMove()));
			}
			catch(IllegalPlacementException e){
				theBoard.putElement(curPosition, this);
				throw new IllegalPlacementException(e.getMessage());
			}
	}
	
	/**
	 * The robot uses the item.
	 * @param 	item
	 * 			The item to be used.
	 * @effect	The given item will be used on the robot.
	 * 			| item.used(this)
	 * @throws  IllegalStateException
	 * 			!this.getItems().contains(item)
	 * @throws  IllegalTerminatedException
	 * 			this.isTerminated() || item.isTerminated()
	 */
	public void use(Item item) throws IllegalStateException{
		if(this.isTerminated() || item.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(this.getItems().contains(item)){
			item.used(this);
		}
		else{
			throw new IllegalStateException();
		}
		
	}

	/**
	 * The robot drops the given item.
	 * @param 	item
	 * 			The item to be dropped.
	 * @effect 	If the robot stands on a board, the item will be put on the board on the same position as the robot.
	 * 			| if this.getBoard!=null
	 * 			|  	then this.getBoard.putElement(this.getPosition(), item)
	 * @post   	The given item will be removed from the robot.
	 * 			| !this.getItems().contains(item)		
	 * @throws	IllegalStateException
	 * 			| !this.getItems.contains(item) 
	 * @throws  IllegalTerminatedException
	 * 			| this.isTerminated() || item.isTerminated()
	 */
	public void drop(Item item) throws IllegalStateException, IllegalTerminatedException{
		if(this.isTerminated() || item.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(this.getItems().contains(item)){
			items.remove(item);
			if(this.getBoard()!=null){
				this.getBoard().putElement(this.getPosition(), item);
			}
		} else{
			throw new IllegalStateException();
		}
		
		
	}
	
	/**
	 * Let the robot shoot one time.
	 * The first element in the line of the shot will be terminated.
	 * @throws	IllegalStateException
	 * 			| this.getBoard() == null
	 * @effect	The energy of the robot will be decreased with ENERGY_FOR_SHOT.
	 * 			| this.setEnergy(this.getEnergy().subtractAndReturn(Robot.getEnergyRequiredToShoot()))
	 * @effect	If their is a element in the direction of the shot, it will be terminated
	 * 			| if (this.getBoard().getFirstElementStartingAt(this.getPosition(), this.getOrientation())!=null)
	 * 			|	then this.getBoard().getFirstElementStartingAt(this.getPosition(), this.getOrientation()).terminate()
	 * @throws  IllegalTerminatedException
	 * 			| this.isTerminated()
	 */
	public void shoot() throws IllegalStateException{
		if(this.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(this.getBoard()!=null){
			this.setEnergy(this.getEnergy().subtractAndReturn(Robot.getEnergyRequiredToShoot()));  
			Element element = this.getBoard().getFirstElementStartingAt(this.getPosition(), this.getOrientation());
			if(element!=null){
				element.hit();
			}
		}
		else{
			throw new IllegalStateException("Robot not placed on a board.");
		}
	}
	
	/**
	 * Returns a string representation of the robot.
	 * @return 	A string representation of the robot.
	 * 			| result == "ROBOT with energy "+this.getEnergy()+"Ws, carrying "+this.getNbOfItems()+" items ("+this.getLoad()+" g)"
	 */
	@Override
	public String toString(){
		return "ROBOT with energy "+this.getEnergy()+", carrying "+this.getNbOfItems()+" items ("+this.getLoad()+" g)";
	}
	
	/**
	 * Returns a list of the items that the robot possess in descending order of their weight.
	 * 
	 * @return	A list of the items that the robot possess in descending order of their weight.
	 * 			| 	for every I,J in 0..result.size()-1:
	 * 			|		if(I < J)
	 * 			|			then result.get(I).getWeight() => result.get(J).getWeight()
	 * 
	 */
	@Basic
	public List<Item> getItems() {  
		return items;
	}
	
	/**
	 * Removes the given item from the robot.
	 * @param 	item
	 * 			The item to be removed from the possessions of the current robot.
	 * @post	The robot no longer possesses the given item.
	 * 			| !this.getItems().contains(item)
	 * @throws 	IllegalArgumentException
	 * 			| !this.getItems().contains(item)
	 * @throws  IllegalTerminatedException
	 * 			this.isTerminated() || item.isTerminated()
	 */
	public void removeItem(Item item) throws IllegalArgumentException, IllegalTerminatedException{
		if(this.isTerminated() || item.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(this.getItems().contains(item)){
			this.getItems().remove(item);
		}
		else{
			throw new IllegalArgumentException();
		}
	}
	
	/**
	 * Returns the ith heaviest item that the robot is carrying.
	 * @param 	i
	 * 			The ith heaviest item that the robot is carrying.
	 * @return	The ith heaviest item that the robot is carrying.
	 * 			| result == this.getItems().get(i)
	 */
	public Item getIthHeaviestItem(int i){
		return this.getItems().get(i);
	}

	/**
	 * Terminates the robot.
	 * 
	 * @effect 	Terminates all the items the robot carries.
	 * 			| for every item in this.getItems(): item.terminate()
	 * @post	The terminated robot doesn't carry any items any more.
	 * 			| new.getNbOfItems() == 0  
	 * @effect	If the robot stands on a board it will be removed from this board.
	 * 			| if (this.getBoard()!=null)
	 * 			|	then this.getBoard().removeElement(this) 
	 * @post	The robot is terminated.
	 * 			| this.isTerminated()
	 */
	@Override
	public void terminate(){
		Set<Item> items = new HashSet<>(this.getItems());
		Iterator<Item> it=items.iterator();
		while(it.hasNext()){
			Item item = it.next();
			this.removeItem(item);
			item.terminate(); 
		}
		if(this.getBoard()!=null){ 
			this.getBoard().removeElement(this);
		}
		this.setTerminate();
	}
	
	/**
	 * Returns the total load the robot is carrying.
	 * @return 	The total load the robot is carrying.
	 * 			|{item in this.getItems() | result == sum(item.getWeight())}
	 * @throws  IllegalTerminatedException
	 * 			| this.isTerminated()
	 */
	public int getLoad(){
		if(this.isTerminated()){
			throw new IllegalTerminatedException();
		}
		Iterator<Item> it = this.getItems().iterator();
		int gewicht = 0;
		while(it.hasNext()){
			gewicht += it.next().getWeight();
		}
		return gewicht;
	}
	
	/**
	 * The robot picks up the item.
	 * @param 	item
	 * 			The item to be picked up.
	 * @post	The robot carries the item.
	 * 			| this.getItems().contains(item)
	 * @effect	The item will be added to the robot
	 * 			| this.addItem(item)
	 * @throws 	IllegalStateException
	 * 			| !this.getPosition().equals(item.getPosition())
	 * @throws  IllegalTerminatedException
	 * 			| this.isTerminated() || item.isTerminated()
	 */
	public void pickUp(Item item) throws IllegalStateException, IllegalTerminatedException{
		if(this.isTerminated() || item.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(this.getPosition().equals(item.getPosition())){
			addItem(item);
		} 
		else {
			throw new IllegalStateException();
		}
	}

	/**
	 * A method for adding an item to the list of possessions of the current robot.
	 * @param 	item
	 * 			The item to be added to the possessions of the current robot.
	 * @throws 	IllegalTerminatedException
	 * 			| this.isTerminated() || item.isTerminated()
	 * @throws 	IllegalArgumentException
	 * 			| !this.canHaveAsItem()
	 * @post	The item will be added to the possessions of the current robot, in a way that the list containing
	 * 			all items is still sorted by weight.
	 * 			| new.getItems().contains(item)
	 * @effect	If the item stands on a board it will be removed from this board.
	 * 			| if item.getBoard()!=null
	 * 			|	then item.getBoard().remove(item)
	 */
	public void addItem(Item item) throws IllegalTerminatedException, IllegalArgumentException{
		if(this.isTerminated() || item.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(item.getBoard()!=null){
			item.getBoard().removeElement(item);
		}
		if(this.canHaveAsItem(item)){
			int i = 0;
			boolean insertion = false;
			while(i<this.getNbOfItems() && !insertion){
				if(item.getWeight()>this.getItems().get(i).getWeight()){
					this.getItems().add(i, item);
					insertion = true;
				}
				i++;
			}
			if(!insertion){
				this.getItems().add(item); 
			}
		} 
		else {
			throw new IllegalArgumentException();
		}
	}
	
	/**
	 * Returns if the robot can carry the item as item.
	 * @param 	item
	 * 			The item to be checked.
	 * @return	True if the item isn't terminated or null and if the robot doesn't contain the item already.
	 * 			result == !item.isTerminated() && !this.getItems().contains(item) && item!=null
	 */
	@Model
	private boolean canHaveAsItem(Item item){
		return item!= null && !item.isTerminated() && !this.getItems().contains(item);
	}
	
	/**
	 * Returns the number of items that the robot carries.
	 * @return 	The number of items that the robot carries.
	 * 			| result == this.getItems.size()
	 */
	public int getNbOfItems(){
		return getItems().size();
	}
	
	/**
	 * 
	 * @param position
	 * @return	Returns -1 whenever the robot is not positioned on a board.
	 * 			| (this.getBoard()==null) == (result == -1) 
	 * @return	Returns -1 whenever the robot is not allowed to stand on the position to be reached.
	 * 			| (this.getMinimalEnergyToReach() throws IllegalStateException) == (result == -1)
	 * @return	Returns -2 whenever the robot has insufficient energy to reach the given position.
	 * 			| (this.getEnergy().isSmallerThan(this.getMinimalCostToReach(position))) == (result == -2)
	 * @return	Returns the amount required to reach the given position if the requirements given above aren't met in Ws.
	 * 			| result == getMinimalCostToReach(position).toEnergyUnit(EnergyUnit.WATTSECONDS).getAmount()
	 * @throws	IllegalTerminatedException
	 * 			When the robot is terminated.
	 * 			| this.isTerminated()
	 */
	public double minEnergyToReachInWattSeconds(Position position) throws IllegalTerminatedException{
		if(this.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(this.getBoard()==null){
			return -1;
		}
		try{
			getMinimalCostToReach(position);
		} catch(IllegalStateException i){
			return -1;
		}
		EnergyAmount energy = getMinimalCostToReach(position);
		if(this.getEnergy().isSmallerThan(energy)){
			return -2;
		}
		return energy.toEnergyUnit(EnergyUnit.WATTSECONDS).getAmount();
	}

	/**
	 * Moves this robot and the other robot as close as possible to each other, taking into account their energy, 
	 * that moving and turning requires energy and that there are obstacles on the board. 
	 * @param 	other
	 * 			The robot which the current robot should get as close as possible to.
	 * @throws	IllegalStateException
	 * 			| this.getBoard()==null || other.getBoard()==null || this.getBoard()!=other.getBoard()
	 * @post	The new positions of both the robots result in a smaller or equal Manhattan distance as the robots before invoking this method, in other
	 * 			words, their distance to one another has decreased.
	 * 			|this.getManhattanDistance(other.getPosition())>=new.getPosition().getManhattanDistance((new other).getPosition())
	 * @post	There is no position reachable for this robot or the other that is closer to each other then the new positions they have both reached, given their
	 * 			amount of energy and the obstacles on the board.
	 * 			| positions = new HashMap().put(new.getPosition(), new HashMap().put(new.getOrientation(), new EnergyAmount()))
	 * 			| 	for each position in new.getEnergyReacheblePositions(positions, (new other).getPosition(), true):
	 * 			|		position.getManhattanDistance((new other).getPosition())>=new.getPosition().getManhattanDistance((new other).getPosition())
	 * 			| positions = new HashMap().put((new other).getPosition(), new HashMap().put((new other).getOrientation(), new EnergyAmount()))
	 * 			| 	for each position in (new other).getEnergyReacheblePositions(positions, new.getPosition(), true):
	 * 			|		position.getManhattanDistance(new.getPosition())>=(new other).getPosition().getManhattanDistance(new.getPosition())
	 * @post	The both robots energy has decreased with the minimal amount of energy necessary to reach their new position.
	 * 			| new.getEnergy() == this.getEnergy().subtractAndReturn(this.getMinimalCostToReach(new.getPosition()))
	 * 			| (new other).getEnergy() == other.getEnergy().subtractAndReturn(other.getMinimalCostToReach((new other).getPosition()))
	 * @throws	IllegalTerminatedException
	 * 			When this robot or other is terminated.
	 * 			| this.isTerminated() || other.isTerminated()
	 */
	public void moveNextTo(Robot other) throws IllegalStateException, IllegalTerminatedException{
		if(this.isTerminated() || other.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(this.getBoard() == null || other.getBoard() == null){
			throw new IllegalStateException("Robot not placed on board.");
		}
		else if(this.getBoard() == other.getBoard()){
			HashMap<Orientation, EnergyAmount> orientone = new HashMap<Orientation, EnergyAmount>();
			orientone.put(this.getOrientation(), new EnergyAmount());
			HashMap<Orientation, EnergyAmount> orienttwo = new HashMap<Orientation, EnergyAmount>();
			orienttwo.put(other.getOrientation(), new EnergyAmount());
			HashMap<Position, HashMap<Orientation, EnergyAmount>> positions1 = new HashMap<Position, HashMap<Orientation, EnergyAmount>>();
			HashMap<Position, HashMap<Orientation, EnergyAmount>> positions2 = new HashMap<Position, HashMap<Orientation, EnergyAmount>>();
			positions1.put(this.getPosition(), orientone);
			positions2.put(other.getPosition(), orienttwo);
			positions1 = this.getEnergyReachablePositions(positions1, other.getPosition(), true);
			positions2 = other.getEnergyReachablePositions(positions2, this.getPosition(), true);
			Object[] result = this.calculateBestPositions(positions1, positions2);
			if(result[0]!=null){
				Board board=this.getBoard();
				board.removeElement(this);
				board.removeElement(other);
				this.setOrientation((Orientation)result[1]);
				other.setOrientation((Orientation)result[4]);
				this.setEnergy(this.getEnergy().subtractAndReturn((EnergyAmount)result[2]));
				other.setEnergy(other.getEnergy().subtractAndReturn((EnergyAmount)result[5]));
				board.putElement((Position)result[0], this);
				board.putElement((Position)result[3], other);
			}
		}
		else{
			throw new IllegalStateException("Robots aren't placed on the same board.");
		}
		
	}
		
	/**
	 * Given the two hashmaps with all the possible reachable positions and with this position another hashmap with all the 
	 * possible orientations and the energy required to reach this position with this orientation. It returns an array of size six where 
	 * the first two objects are the best position and orientation for this robot and as third object the minimal energy required to reach these. 
	 * The next two objects are the best position and orientation for the given robot and the last object is the energy required to reach these.
	 * @param 	positions1
	 * 			The first hashmap with the necessary data.
	 * @param 	positions2
	 * 			The second hashmap with the necessary data.
	 * @return	An array where the first three elements are the best position, orientation an the energy necessary to reach these for positions1.
	 * 			The next three elements are the best position, orientation an the energy necessary to reach these for positions2. 
	 * 			| result[2]=positions1.get(result[0]).get(result[1])
	 * 			| result[5]=positions2.get(result[3]).get(result[4])
	 * 			Their will be no combination of positions from positions1 and positions2 that will have a smaller Manhattan distance (that is not zero) 
	 * 			than the Manhattan distance between the first and the fourth element from the result. 
	 * 			| for each positionOne in positions1 and positionsTwo in positions2:
	 * 			|	if positionOne.getManhattanDistance(positionsTwo)!=0
	 * 			|		then	result[0].getManhattanDistance(result[3]) <= positionOne.getManhattanDistance(positionsTwo)
	 * 			And if the Manhattan distance is the same, their will be no orientations for this positions where the required energy for these are smaller 
	 * 			than the sum of the third and the sixth element from the result.
	 * 			|				if(result[0].getManhattanDistance(result[3]) == positionOne.getManhattanDistance(positionsTwo)
	 * 			|					then 	for each orientationOne in positions1.get(positionOne) and orientationTwo in positions2.get(positionTwo):
	 * 			|								result[2].addAndReturn(result[5]).isSmallerOrEqual(positions1.get(positionOne).get(orientationOne).addAndeReturn(positions2.get(positionTwo).get(orientationTwo)))
	 * @throws	IllegalTerminatedException
	 * 			When the robot is terminated.
	 * 			| this.isTerminated()
	 */
	@Model
	private Object[] calculateBestPositions(HashMap<Position, HashMap<Orientation, EnergyAmount>> positions1, HashMap<Position, HashMap<Orientation, EnergyAmount>> positions2) throws IllegalTerminatedException{
		if(this.isTerminated()){
			throw new IllegalTerminatedException();
		}
		Position pone = null;
		Position ptwo = null;
		Orientation oone = null;
		Orientation otwo = null;
		EnergyAmount eone = null;
		EnergyAmount etwo = null;
		Iterator<Position> itone = positions1.keySet().iterator();
		while(itone.hasNext()){
			Position firstPosition = itone.next();
			Iterator<Position> ittwo = positions2.keySet().iterator();
			while (ittwo.hasNext()) {
				Position secondPosition = ittwo.next();
				if(firstPosition.getManhattanDistance(secondPosition)!=0 && (pone == null || firstPosition.getManhattanDistance(secondPosition)<pone.getManhattanDistance(ptwo))){
					pone=firstPosition;
					ptwo=secondPosition;
					Iterator<Orientation> itorientone = positions1.get(firstPosition).keySet().iterator();
					Iterator<Orientation> itorienttwo = positions2.get(secondPosition).keySet().iterator();
					oone = itorientone.next();
					eone = positions1.get(firstPosition).get(oone);
					otwo = itorienttwo.next();
					etwo = positions2.get(secondPosition).get(otwo);
					while(itorientone.hasNext()){
						Orientation orient = itorientone.next();
						if(positions1.get(firstPosition).get(orient).isSmallerThan(eone)){
							oone = orient;
							eone = positions1.get(firstPosition).get(orient);
						}
					}
					while(itorienttwo.hasNext()){
						Orientation orient = itorienttwo.next();
						if(positions2.get(secondPosition).get(orient).isSmallerThan(etwo)){
							otwo = orient;
							etwo = positions2.get(secondPosition).get(orient);
						}
					}
				} else if(firstPosition.getManhattanDistance(secondPosition)!=0 && firstPosition.getManhattanDistance(secondPosition)==pone.getManhattanDistance(ptwo)){
					Iterator<Orientation> itorientone = positions1.get(firstPosition).keySet().iterator();
					Iterator<Orientation> itorienttwo = positions2.get(secondPosition).keySet().iterator();
					Orientation bestone = itorientone.next();
					Orientation besttwo = itorienttwo.next();
					while(itorientone.hasNext()){
						Orientation orient = itorientone.next();
						if(positions1.get(firstPosition).get(orient).isSmallerThan(positions1.get(firstPosition).get(bestone))){
							bestone = orient;
						}
					}
					while(itorienttwo.hasNext()){
						Orientation orient = itorienttwo.next();
						if(positions2.get(secondPosition).get(orient).isSmallerThan(positions2.get(secondPosition).get(besttwo))){
							besttwo = orient;
						}
					}
					if(positions1.get(firstPosition).get(bestone).addAndReturn(positions2.get(secondPosition).get(besttwo)).isSmallerThan(positions1.get(pone).get(oone).addAndReturn(positions2.get(ptwo).get(otwo)))){
						pone=firstPosition;
						ptwo=secondPosition;
						oone=bestone;
						otwo=besttwo;
						eone=positions1.get(firstPosition).get(bestone);
						etwo=positions2.get(secondPosition).get(besttwo);
					}
				}
			}
		}
		return new Object[] {pone, oone, eone, ptwo, otwo, etwo};
	}
	
	/**
	 * Returns the minimal amount of energy to reach the given position for this robot.
	 * @param 	position
	 * 			The position the robot should reach.
	 * @throws	IllegalStateException
	 * 			When the robot isn't allowed to share the given position with the element already occupying 
	 * 			the given position.
	 * 			| for each element in this.getElements(position):
	 * 			|	if (!this.canSharePositionWith(element))
	 * @throws	IllegalStateException
	 * 			Whenever the given position is impossible to reach due to obstacles 
	 * 			preventing the robot to move to the given position.
	 * 			| !this.getReachablePositions(new HashMap<Position, HashMap<Orientation, Double>>().put(this.getPosition(), new HashMap<Orientation, Double>().put(this.getOrientation(), 0.0)),position,false).containsKey(position)
	 * @return	The minimal amount of energy required to reach the given position if this position is reachable.
	 * 			| for each possible path (combination of turns and moves) to the given position:
	 * 			|	result.isSmallerOrEqualTo(the energy required for this path)
	 * @throws	IllegalTerminatedException
	 * 			When the robot is terminated
	 * 			| this.isTerminated()
	 * */
	@Model
	private EnergyAmount getMinimalCostToReach(Position position) throws IllegalStateException, IllegalTerminatedException{ 
		if(this.isTerminated()){
			throw new IllegalTerminatedException();
		}
		Iterator<Element> itElement = this.getBoard().getElements(position).iterator();
		while (itElement.hasNext()){
			if(!this.canSharePositionWith(itElement.next())){
				throw new IllegalStateException();
			}
		}
		HashMap<Orientation, EnergyAmount> orientone = new HashMap<Orientation, EnergyAmount>();
		orientone.put(this.getOrientation(), new EnergyAmount());
		HashMap<Position, HashMap<Orientation, EnergyAmount>> positions1 = new HashMap<Position, HashMap<Orientation, EnergyAmount>>();
		positions1.put(this.getPosition(), orientone);
		positions1 = this.getEnergyReachablePositions(positions1, position, false);
		if(!positions1.containsKey(position)){
			throw new IllegalStateException();
		}
		Iterator<Orientation> it = positions1.get(position).keySet().iterator();
		EnergyAmount energy = positions1.get(position).get(it.next());
		while(it.hasNext()){
			EnergyAmount energytry=positions1.get(position).get(it.next());
			if(energytry.isSmallerThan(energy)){
				energy=energytry;
			}
		}
		return energy;
	}
	
	/**
	 * @param 	positions
	 * 			A list of previously calculated positions the robot can reach.  
	 * @param 	position
	 * 			The position we try to reach.
	 * @param 	stopAtEnergy
	 * 			If the calculation should take into account that the robot is limited by its current amount of energy.
	 * @return	A hashmap with all the reachable positions for this robot, taking into account that their are obstacles on the board and if stopAtEnergy is true also taking into account the robot's energy. 
	 * 			If the robot can reach the given position, the hashmap will only contain positions that require less or an equal amount of moves. The value of these positions are hashmaps with orientations 
	 * 			and the amount of energy that is minimally required to reach the position with the orientation.
	 * 			| for each position in result:
	 * 			|		their exists a path (combination of moves and turns) to reach this position
	 * 			|		for each possible path to reach this position:
	 * 			|			 Collection.min(result.get(position).values()).isSmallerOrEqualTo(the energy necessary for this path)
	 * 			If stopAtEnergy is true, there will be no position and orientation that requires more energy than the energy of the robot.
	 * 			| if stopAtEnergy
	 * 			|	then 	for each position in result:
	 * 			|				for each orientation in result.get(position):
	 * 			|					result.get(position).get(orientation).isSmallerOrEqualTo(this.getEnergy())
	 * @throws	IllegalTerminatedException
	 * 			When the robot is terminated.
	 * 			| this.isTerminated()
	 */
	@Model
	private HashMap<Position, HashMap<Orientation, EnergyAmount>> getEnergyReachablePositions(HashMap<Position, HashMap<Orientation, EnergyAmount>> positions, Position position, boolean stopAtEnergy) throws IllegalTerminatedException{
		if(this.isTerminated()){
			throw new IllegalTerminatedException();
		}
		HashMap<Position, HashMap<Orientation, EnergyAmount>> positionscopy = new HashMap<Position, HashMap<Orientation, EnergyAmount>>(positions);
		Iterator<Position> it = positionscopy.keySet().iterator();
		boolean end = true;
		while(it.hasNext()){
			Position pos= it.next();
			HashMap<Orientation,EnergyAmount> value = positions.get(pos);
			Iterator<Orientation> orientit = value.keySet().iterator();
			while(orientit.hasNext()){
				EnergyAmount myEnergy=this.getEnergy();
				if(!stopAtEnergy){
					myEnergy=this.getMaxEnergy();
				}
				Orientation orient = orientit.next();
				EnergyAmount energy = value.get(orient);
				Orientation orientCW = orient.turnClockwise();
				Orientation orientCCW = orient.turnCounerClockwise();
				Orientation orient2CW = orient.turnClockwise().turnClockwise();
				Position one = pos.getNeighbour(orient);
				EnergyAmount energyone= (energy.addAndReturn(this.getEnergyForMove()));											
				boolean useone = !positionscopy.containsKey(one) && this.getBoard().isValidPosition(one, this) && this.isLegalEnergy(myEnergy.subtractAndReturn(energyone)); 
				Position two = pos.getNeighbour(orientCW);
				EnergyAmount energytwo = (energy.addAndReturn(this.getEnergyForMove()).addAndReturn(Robot.getEnergyForTurn()));
				boolean usetwo = !positionscopy.containsKey(two) && this.getBoard().isValidPosition(two, this) && this.isLegalEnergy(myEnergy.subtractAndReturn(energytwo)) ; 
				Position three = pos.getNeighbour(orientCCW);
				EnergyAmount energythree = (energy.addAndReturn(this.getEnergyForMove()).addAndReturn(Robot.getEnergyForTurn()));
				boolean usethree = !positionscopy.containsKey(three) && this.getBoard().isValidPosition(three, this) && this.isLegalEnergy(myEnergy.subtractAndReturn(energythree)) ; 
				Position four = pos.getNeighbour(orient2CW);
				EnergyAmount energyfour = (energy.addAndReturn(this.getEnergyForMove()).addAndReturn(Robot.getEnergyForTurn().multiplyAndReturn(2)));
				boolean usefour = !positionscopy.containsKey(four) && this.getBoard().isValidPosition(four, this) && this.isLegalEnergy(myEnergy.subtractAndReturn(energyfour)) ; 
				if(useone){
					end=false;
					HashMap<Orientation,EnergyAmount> map = new HashMap<Orientation,EnergyAmount>();
					if(positions.containsKey(one)){
						map = positions.get(one);
					}
					map.put(orient, energyone);
					positions.put(one, map);
				}
				if(usetwo){
					end=false;
					HashMap<Orientation,EnergyAmount> map = new HashMap<Orientation,EnergyAmount>();
					if(positions.containsKey(two)){
						map = positions.get(two);
					}
					map.put(orientCW, energytwo);
					positions.put(two, map);
				}
				if(usethree){
					end=false;
					HashMap<Orientation,EnergyAmount> map = new HashMap<Orientation,EnergyAmount>();
					if(positions.containsKey(three)){
						map = positions.get(three);
					}
					map.put(orientCCW, energythree);
					positions.put(three, map);
				}
				if(usefour){
					end=false;
					HashMap<Orientation,EnergyAmount> map = new HashMap<Orientation,EnergyAmount>();
					if(positions.containsKey(four)){
						map = positions.get(four);
					}
					map.put(orient2CW, energyfour);
					positions.put(four, map);
				}
			}
		}
		if(!positions.containsKey(position) && !end){
			this.getEnergyReachablePositions(positions, position, stopAtEnergy);
		}
		return positions;
	}
	
	/**
	 * This method deals with a robot being hit by a laser.
	 * @throws	IllegalTerminatedException
	 * 			When the current robot is terminated.
	 * 			| this.isTerminated()
	 * @effect	The maximum energy of the robot is decreased by 4000 Ws.
	 * 			| this.decreaseMaxEnergy(new EnergyAmount(4000,EnergyUnit.WATTSECONDS))
	 * 
	 */
	@Override
	public void hit() throws IllegalTerminatedException{
		if(this.isTerminated()){
			throw new IllegalTerminatedException();
		}
		this.decreaseMaxEnergy(new EnergyAmount(4000,EnergyUnit.WATTSECONDS));
	}
	
	/**
	 * The current robot transfers all of its items to robot. 
	 * @param 	robot
	 * @throws 	IllegalStateException
	 * 			When robot is not standing next to the current robot.
	 * 			| !this.getPosition().isNeighbour(robot.getPosition())
	 * @throws	IllegalTerminatedException
	 * 			When the current robot or robot is terminated.
	 * 			| this.isTerminated() || robot.isTerminated()
	 * @post	The current robot has no possessions after invoking this method.	
	 * 			| new.getNbOfItem() == 0
	 * @post	The robot transfers all if its items to robot if and only is robot is standing next to the current robot.
	 * 			| for each item in this.getItems():
	 * 			|	! new.getItems().contains(item) && (new robot).getItems().contains(item)
	 */
	public void transferItems(Robot robot) throws IllegalStateException, IllegalTerminatedException{
		if(this.isTerminated() || robot.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(this.getPosition().isNeighbour(robot.getPosition())){
			Iterator<Item> it = this.getItems().iterator();
			while(it.hasNext()){
				Item item = it.next();
				it.remove();
				robot.addItem(item);
			}
			assert(this.getNbOfItems()==0);
		}
		else{
			throw new IllegalStateException();
		}
	}
	
	/**
	 * Sets a given entry as the program of this robot.
	 * @param 	entry
	 * 			The program the robot will contain.
	 * @pre		The given entry is a legal program.
	 * 			| this.canHaveAsProgram(entry)
	 * @post	The program will be stored in the current robot.
	 * 			| new.getSavedProgram() == entry
	 */
	@Model
	private void setProgram(Entry entry){
		assert(canHaveAsProgram(entry));
		this.program = entry;
	}
	
	/**
	 * Checks whether a given entry can be stored in the robot.
	 * @param	entry
	 * 			The entry to be checked.
	 * @return	True only if entry is not a null object.
	 * 			| result == entry != null
	 */
	@Model
	private boolean canHaveAsProgram(Entry entry){
		return entry != null;
	}
	
	/**
	 * Saves a given input as a program into the current robot.
	 * @param 	input
	 * 			The input we want to save into the current robot.
	 * @throws 	IllegalArgumentException
	 * 			When the program can't be added to the current robot.
	 * 			| !this.canProgBeAdded(input)
	 * @throws 	IllegalTerminatedException
	 * 			When the current robot is terminated.
	 * 			| this.isTerminated()
	 * @effect	Stores a given input as a program in the current robot.
	 * 			| this.setProgram(Compiler.compile(entry))
	 */
	public void saveProg(String input) throws IllegalArgumentException, IllegalTerminatedException{
		if(this.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(this.canProgBeAdded(input)){
			Entry entry = Compiler.compile(input);
			this.setProgram(entry);
		}
		else{
			throw new IllegalArgumentException();
		}
	}
	
	/**
	 * Checks whether a given input is valid.
	 * @param 	input
	 * 			The input to be checked.
	 * @return	True only if the given input is not equals to null.
	 * 			| result == input!=null
	 */
	@Model
	private boolean canProgBeAdded(String input){
		return input!=null;
	}
	
	/**
	 * Returns the stored program of the current robot.
	 * @return	The stored program of the current robot.
	 * 			| result == this.program
	 */
	@Basic
	public Entry getSavedProgram(){
		return this.program;
	}
	
	/**
	 * Executes the stored program of the current robot.
	 * @throws	IllegalStateException
	 * 			When the robot doesn't have a program stored.
	 * 			| this.getSavedProgram() == null
	 * @effect	Executes the stored program.	
	 *			| this.getSavedProgram().execute(this)
	 * @throws	IllegalTerminatedException
	 * 			| this.isTerminated()
	 */
	public void executeMyProg() throws IllegalStateException, IllegalTerminatedException{
		if(this.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(this.getSavedProgram()==null){
			throw new IllegalStateException();
		}
		if(this.getSavedProgram() instanceof Command){
			((Command) this.getSavedProgram()).execute(this);
		}
	}
	
	/**
	 * Executes a single step of the stored program, that is until it encounters a basic 
	 * command or until it reaches a final state (i.e. the empty sequence).
	 * @throws 	IllegalTerminatedException
	 * 			When the current robot is terminated.
	 * 			| this.isTerminated()
	 * @throws 	IllegalStateException
	 * 			When the robot doesn't have a stored program
	 * 			| this.getSavedProgram() == null
	 * @effect	Executes a single step of the stored program. If the stored program is totally finished is sets the program back on totally undone.
	 * 			| this.getSavedProgram().executeSingleStep(this)
	 * 			| (this.getSavedProgram().isAlreadyExecuted()) == (this.getSavedProgram().setAlreadyExecuted(false))
	 */
	public void executeSingleStepOfProg() throws IllegalTerminatedException, IllegalStateException{
		if(this.isTerminated()){
			throw new IllegalTerminatedException();
		}
		if(this.getSavedProgram()==null){
			throw new IllegalStateException();
		}
		if((this.getSavedProgram() instanceof Command)){
			((Command) this.getSavedProgram()).executeSingleStep(this);
			if(((Command) this.getSavedProgram()).isAlreadyExecuted()){
				((Command) this.getSavedProgram()).setAlreadyExecuted(false);
			}
		}
		
	}
	
	/**
	 * Teleports the current robot to a totally random position within the board's boundaries.
	 * @post	The current robot has a new position without consuming energy to reach it.
	 * 			| !this.getPosition().equals(this.getPosition) && this.getEnergy() == new.getEnergy()
	 * 			|		&& this.getBoard().isValidPosition(new.getPosition(),this)
	 */
	public void teleport(){
		Random random = new Random();
		Position position = new Position(-1, -1);
		Position curPosition = this.getPosition();
		Board board = this.getBoard();
		while(!board.isValidPosition(position, this) && !position.equals(curPosition)){
			position.setX((new Double(random.nextDouble()*board.getWidth())).longValue());
			position.setY((new Double(random.nextDouble()*board.getHeight())).longValue());
		}
		board.removeElement(this);
		board.putElement(position, this);
	}
}